/*
 * copy.c
 *
 *  Created on: May 24, 2021
 *      Author: kirk_
 */

#include "copy.h"
#include "ov5640.h"
#include "sccb.h"


//////////////////////////////////////////////////////////////////////////////////
//本程序只供学习使用，未经作者许可，不得用于其它任何用途
//ALIENTEK STM32H7开发板
//DCMI驱动代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2017/8/15
//版本：V1.0
//版权所有，盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//////////////////////////////////////////////////////////////////////////////////

DCMI_HandleTypeDef  DCMI_Handler;           //DCMI句柄
DMA_HandleTypeDef   DMADMCI_Handler;        //DMA句柄

u8 ov_frame=0;  						//帧率
extern void jpeg_data_process(void);	//JPEG数据处理函数

//DCMI初始化
void DCMI_Init(void)
{
    DCMI_Handler.Instance=DCMI;
    DCMI_Handler.Init.SynchroMode=DCMI_SYNCHRO_HARDWARE;    //硬件同步HSYNC,VSYNC
    DCMI_Handler.Init.PCKPolarity=DCMI_PCKPOLARITY_RISING;  //PCLK 上升沿有效
    DCMI_Handler.Init.VSPolarity=DCMI_VSPOLARITY_LOW;       //VSYNC 低电平有效
    DCMI_Handler.Init.HSPolarity=DCMI_HSPOLARITY_LOW;       //HSYNC 低电平有效
    DCMI_Handler.Init.CaptureRate=DCMI_CR_ALL_FRAME;        //全帧捕获
    DCMI_Handler.Init.ExtendedDataMode=DCMI_EXTEND_DATA_8B; //8位数据格式
    HAL_DCMI_Init(&DCMI_Handler);                           //初始化DCMI，此函数会开启帧中断

    HAL_NVIC_SetPriority(DCMI_IRQn,2,2);        //抢占优先级1，子优先级2
    HAL_NVIC_EnableIRQ(DCMI_IRQn);              //使能DCMI中断
    //关闭行中断、VSYNC中断、同步错误中断和溢出中断
    //__HAL_DCMI_DISABLE_IT(&DCMI_Handler,DCMI_IT_LINE|DCMI_IT_VSYNC|DCMI_IT_ERR|DCMI_IT_OVR);

	//关闭所有中断，函数HAL_DCMI_Init()会默认打开很多中断，开启这些中断
	//以后我们就需要对这些中断做相应的处理，否则的话就会导致各种各样的问题，
	//但是这些中断很多都不需要，所以这里将其全部关闭掉，也就是将IER寄存器清零。
	//关闭完所有中断以后再根据自己的实际需求来使能相应的中断。
	DCMI->IER=0x0;

    __HAL_DCMI_ENABLE_IT(&DCMI_Handler,DCMI_IT_FRAME);      //使能帧中断
    __HAL_DCMI_ENABLE(&DCMI_Handler);                       //使能DCMI
}



//DCMI DMA配置
//mem0addr:存储器地址0  将要存储摄像头数据的内存地址(也可以是外设地址)
//mem1addr:存储器地址1  当只使用mem0addr的时候,该值必须为0
//memblen:存储器位宽,可以为:DMA_MDATAALIGN_BYTE/DMA_MDATAALIGN_HALFWORD/DMA_MDATAALIGN_WORD
//meminc:存储器增长方式,可以为:DMA_MINC_ENABLE/DMA_MINC_DISABLE
void DCMI_DMA_Init(u32 mem0addr,u32 mem1addr,u16 memsize,u32 memblen,u32 meminc)
{
    __HAL_RCC_DMA1_CLK_ENABLE();                                    //使能DMA1时钟
    __HAL_LINKDMA(&DCMI_Handler,DMA_Handle,DMADMCI_Handler);        //将DMA与DCMI联系起来
	__HAL_DMA_DISABLE_IT(&DMADMCI_Handler,DMA_IT_TC);    			//先关闭DMA传输完成中断(否则在使用MCU屏的时候会出现花屏的情况)

    DMADMCI_Handler.Instance=DMA1_Stream1;                          //DMA1数据流1
	DMADMCI_Handler.Init.Request=DMA_REQUEST_DCMI;					//DCMI的DMA请求
    DMADMCI_Handler.Init.Direction=DMA_PERIPH_TO_MEMORY;            //外设到存储器
    DMADMCI_Handler.Init.PeriphInc=DMA_PINC_DISABLE;                //外设非增量模式
    DMADMCI_Handler.Init.MemInc=meminc;                   			//存储器增量模式
    DMADMCI_Handler.Init.PeriphDataAlignment=DMA_PDATAALIGN_WORD;   //外设数据长度:32位
    DMADMCI_Handler.Init.MemDataAlignment=memblen;     				//存储器数据长度:8/16/32位
    DMADMCI_Handler.Init.Mode=DMA_CIRCULAR;                         //使用循环模式
    DMADMCI_Handler.Init.Priority=DMA_PRIORITY_HIGH;                //高优先级
    DMADMCI_Handler.Init.FIFOMode=DMA_FIFOMODE_ENABLE;              //使能FIFO
    DMADMCI_Handler.Init.FIFOThreshold=DMA_FIFO_THRESHOLD_HALFFULL; //使用1/2的FIFO
    DMADMCI_Handler.Init.MemBurst=DMA_MBURST_SINGLE;                //存储器突发传输
    DMADMCI_Handler.Init.PeriphBurst=DMA_PBURST_SINGLE;             //外设突发单次传输
    HAL_DMA_DeInit(&DMADMCI_Handler);                               //先清除以前的设置
    HAL_DMA_Init(&DMADMCI_Handler);	                                //初始化DMA

    //在开启DMA之前先使用__HAL_UNLOCK()解锁一次DMA,因为HAL_DMA_Statrt()HAL_DMAEx_MultiBufferStart()
    //这两个函数一开始要先使用__HAL_LOCK()锁定DMA,而函数__HAL_LOCK()会判断当前的DMA状态是否为锁定状态，如果是
    //锁定状态的话就直接返回HAL_BUSY，这样会导致函数HAL_DMA_Statrt()和HAL_DMAEx_MultiBufferStart()后续的DMA配置
    //程序直接被跳过！DMA也就不能正常工作，为了避免这种现象，所以在启动DMA之前先调用__HAL_UNLOC()先解锁一次DMA。
    __HAL_UNLOCK(&DMADMCI_Handler);
    if(mem1addr==0)    //开启DMA，不使用双缓冲
    {
        HAL_DMA_Start(&DMADMCI_Handler,(u32)&DCMI->DR,mem0addr,memsize);
    }
    else                //使用双缓冲
    {
        HAL_DMAEx_MultiBufferStart(&DMADMCI_Handler,(u32)&DCMI->DR,mem0addr,mem1addr,memsize);//开启双缓冲
        __HAL_DMA_ENABLE_IT(&DMADMCI_Handler,DMA_IT_TC);    //开启传输完成中断
        HAL_NVIC_SetPriority(DMA1_Stream1_IRQn,2,3);        //DMA中断优先级
        HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);
    }
}

//DCMI,启动传输
void DCMI_Start(void)
{
    __HAL_DMA_ENABLE(&DMADMCI_Handler); //使能DMA
    DCMI->CR|=DCMI_CR_CAPTURE;          //DCMI捕获使能
}

//DCMI,关闭传输
void DCMI_Stop(void)
{
    DCMI->CR&=~(DCMI_CR_CAPTURE);       //关闭捕获
    while(DCMI->CR&0X01);               //等待传输完成
    __HAL_DMA_DISABLE(&DMADMCI_Handler);//关闭DMA
}

//DCMI中断服务函数
void DCMI_IRQHandler(void)
{
    HAL_DCMI_IRQHandler(&DCMI_Handler);
}

//捕获到一帧图像处理函数
//hdcmi:DCMI句柄
void HAL_DCMI_FrameEventCallback(DCMI_HandleTypeDef *hdcmi)
{


	ov_frame++;
    //重新使能帧中断,因为HAL_DCMI_IRQHandler()函数会关闭帧中断
    __HAL_DCMI_ENABLE_IT(&DCMI_Handler,DCMI_IT_FRAME);
}

void (*dcmi_rx_callback)(void);//DCMI DMA接收回调函数
//DMA1数据流1中断服务函数
void DMA1_Stream1_IRQHandler(void)
{
    if(__HAL_DMA_GET_FLAG(&DMADMCI_Handler,DMA_FLAG_TCIF1_5)!=RESET)//DMA传输完成
    {
        __HAL_DMA_CLEAR_FLAG(&DMADMCI_Handler,DMA_FLAG_TCIF1_5);//清除DMA传输完成中断标志位
        dcmi_rx_callback();	//执行摄像头接收回调函数,读取数据等操作在这里面处理
    }
}
////////////////////////////////////////////////////////////////////////////////
//以下两个函数,供usmart调用,用于调试代码

//DCMI设置显示窗口
//sx,sy;LCD的起始坐标
//width,height:LCD显示范围.
void DCMI_Set_Window(u16 sx,u16 sy,u16 width,u16 height)
{
	DCMI_Stop();

	OV5640_OutSize_Set(0,0,width,height);
    __HAL_DMA_ENABLE(&DMADMCI_Handler); //开启DMA1,Stream1
    DCMI->CR|=DCMI_CR_CAPTURE;          //DCMI捕获使能
}

//通过usmart调试,辅助测试用.
//pclk/hsync/vsync:三个信号的有限电平设置
void DCMI_CR_Set(u8 pclk,u8 hsync,u8 vsync)
{
    HAL_DCMI_DeInit(&DCMI_Handler);//清除原来的设置
    DCMI_Handler.Instance=DCMI;
    DCMI_Handler.Init.SynchroMode=DCMI_SYNCHRO_HARDWARE;//硬件同步HSYNC,VSYNC
    DCMI_Handler.Init.PCKPolarity=pclk<<5;              //PCLK 上升沿有效
    DCMI_Handler.Init.VSPolarity=vsync<<7;              //VSYNC 低电平有效
    DCMI_Handler.Init.HSPolarity=hsync<<6;              //HSYNC 低电平有效
    DCMI_Handler.Init.CaptureRate=DCMI_CR_ALL_FRAME;    //全帧捕获
    DCMI_Handler.Init.ExtendedDataMode=DCMI_EXTEND_DATA_8B;//8位数据格式
    HAL_DCMI_Init(&DCMI_Handler);                       //初始化DCMI
    DCMI_Handler.Instance->CR|=DCMI_MODE_CONTINUOUS;    //持续模式
}


uint16_t *dcmi_line_buf[2] = {(uint16_t*)0x24000000, (uint16_t*)0x24000400};
u16 curline = 0;
u16 yoffset=0;							//y方向的偏移量
u16 outputheight;

//RGB屏数据接收回调函数
void rgblcd_dcmi_rx_callback(void)
{
    u16 *pbuf;
    if(DMA1_Stream1->CR&(1<<19))//DMA使用buf1,读取buf0
    {
        pbuf=(u16*)dcmi_line_buf[0];
    } else 						//DMA使用buf0,读取buf1
    {
        pbuf=(u16*)dcmi_line_buf[1];
    }
    memcpy(Display[curline], pbuf, 1600);
    if(curline<600)curline++;
    else curline = 0;
}
//RGB565测试
//RGB数据直接显示在LCD上面
void rgb565_test(void)
{

    //自动对焦初始化
    OV5640_RGB565_Mode();	//RGB565模式
    OV5640_Focus_Init();
    OV5640_Light_Mode(0);	//自动模式
    OV5640_Color_Saturation(3);//色彩饱和度0
    OV5640_Brightness(4);	//亮度0
    OV5640_Contrast(3);		//对比度0
    OV5640_Sharpness(33);	//自动锐度
    OV5640_Focus_Constant();//启动持续对焦
    DCMI_Init();			//DCMI配置

        dcmi_rx_callback=rgblcd_dcmi_rx_callback;//RGB屏接收数据回调函数
        DCMI_DMA_Init((u32)dcmi_line_buf[0],(u32)dcmi_line_buf[1],800/2,DMA_MDATAALIGN_HALFWORD,DMA_MINC_ENABLE);



        yoffset=0;
        outputheight=600;
        OV5640_WR_Reg(0x3035,0X51);

    curline=yoffset;		//行数复位
    OV5640_OutSize_Set(4,0,800,600);		//满屏缩放显示
    DCMI_Start(); 			//启动传输
#if 0
    while(1)
    {
        key=KEY_Scan(0);
        if(key)
        {
            if(key!=KEY1_PRES)DCMI_Stop(); //非KEY1按下,停止显示
            switch(key)
            {
            case KEY0_PRES:	//对比度设置
                contrast++;
                if(contrast>6)contrast=0;
                OV5640_Contrast(contrast);
                sprintf((char*)msgbuf,"Contrast:%d",(signed char)contrast-3);
                break;
            case KEY1_PRES:	//执行一次自动对焦
                OV5640_Focus_Single();
                break;
            case KEY2_PRES:	//特效设置
                effect++;
                if(effect>6)effect=0;
                OV5640_Special_Effects(effect);//设置特效
                sprintf((char*)msgbuf,"%s",EFFECTS_TBL[effect]);
                break;
            case WKUP_PRES:	//1:1尺寸(显示真实尺寸)/缩放
                scale=!scale;
                if(scale==0)
                {
                    fac=(float)800/outputheight;	//得到比例因子
                    OV5640_OutSize_Set((1280-fac*lcddev.width)/2,(800-fac*outputheight)/2,lcddev.width,outputheight);
                    sprintf((char*)msgbuf,"Full Size 1:1");
                } else
                {
                    OV5640_OutSize_Set(4,0,lcddev.width,outputheight);
                    sprintf((char*)msgbuf,"Scale");
                }
                break;
            }
            if(key!=KEY1_PRES)	//非KEY1按下
            {
                LCD_ShowString(30,50,210,16,16,msgbuf);//显示提示内容
                delay_ms(800);
                DCMI_Start();	//重新开始传输
            }
        }
        delay_ms(10);
    }
#endif
}
